package com.csit.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.util.Assert;

import com.csit.bean.Pager;
import com.csit.bean.Pager.OrderType;
import com.csit.dao.BaseDao;
import com.csit.util.BeanUtil;

/**
 * @author longweier
 * @Description: BaseDao的实现类
 * @Copyright: 福州骏华科技信息有限公司 (c)2012
 * @Created Date : 2012-3-28
 * @vesion 1.0
 */
public abstract class BaseDaoImpl<T, PK extends Serializable> implements BaseDao<T, PK> {

	private Class<T> entityClass;
	@Resource
	protected SessionFactory sessionFactory;

	@SuppressWarnings("unchecked")
	public BaseDaoImpl() {
		this.entityClass = null;
		Class c = getClass();
		Type type = c.getGenericSuperclass();
		if (type instanceof ParameterizedType) {
			Type[] parameterizedType = ((ParameterizedType) type).getActualTypeArguments();
			this.entityClass = (Class<T>) parameterizedType[0];
		}
	}

	protected void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	protected Session getSession() {
		return sessionFactory.getCurrentSession();
	}

	/*
	 * (non-Javadoc)
	 * @see com.csit.dao.BaseDao#clear()
	 */
	public void clear() {
		this.getSession().clear();
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#delete(java.lang.Object)
	 */
	public void delete(T entity) {
		Assert.notNull(entity, "entity is required");
		this.getSession().delete(entity);

	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#delete(java.io.Serializable)
	 */
	public void delete(PK id) {
		Assert.notNull(id, "id is required");
		T entity = load(id);
		delete(entity);
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#delete(PK[])
	 */
	public void delete(PK[] ids) {
		Assert.notNull(ids, "ids is required");

		for (PK id : ids) {
			delete(id);
		}
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#evict(java.lang.Object)
	 */
	public void evict(Object object) {
		Assert.notNull(object, "object is required");
		this.getSession().evict(object);

	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#findByPager(com.csit.bean.Pager)
	 */
	public Pager findByPager(Pager pager,Boolean countTotal) {
		if (pager == null) {
			pager = new Pager();
		}
		DetachedCriteria detachedCriteria = DetachedCriteria.forClass(entityClass);
		return findByPager(pager, detachedCriteria,countTotal);
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#findByPager(com.csit.bean.Pager, org.hibernate.criterion.DetachedCriteria)
	 */
	public Pager findByPager(Pager pager, DetachedCriteria detachedCriteria,Boolean countTotal) {
		if (pager == null) {
			pager = new Pager();
		}
		Integer pageSize = pager.getPageSize();
		if(pageSize==null||pageSize<0){
			pageSize = DEFAULTPAGESIZE;
		}
		String property = pager.getProperty();
		String keyword = pager.getKeyword();
		String orderBy = pager.getOrderBy();
		OrderType orderType = pager.getOrderType();

		Criteria criteria = detachedCriteria.getExecutableCriteria(getSession());

		if (StringUtils.isNotEmpty(property) && StringUtils.isNotEmpty(keyword)) {
			String propertyString = "";
			if (property.contains(".")) {
				String propertyPrefix = StringUtils.substringBefore(property, ".");
				String propertySuffix = StringUtils.substringAfter(property, ".");
				criteria.createAlias(propertyPrefix, "model");
				propertyString = "model." + propertySuffix;
			} else {
				propertyString = property;
			}
			if (keyword != null && StringUtils.isNotEmpty(keyword.toString())) {

				criteria.add(Restrictions.like(propertyString, "%" + keyword + "%"));
			}
		}

		Long totalCount = 0L;
		
		if(countTotal){
			totalCount = (Long) criteria.setProjection(Projections.rowCount()).uniqueResult();
		}

		criteria.setProjection(null);
		criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		criteria.setFirstResult(pager.getBeginCount());
		criteria.setMaxResults(pageSize);
		if (StringUtils.isNotEmpty(orderBy) && orderType != null) {
			if (orderType == OrderType.asc) {
				criteria.addOrder(Order.asc(orderBy));
			} else {
				criteria.addOrder(Order.desc(orderBy));
			}
		}
		pager.setTotalCount(totalCount);
		pager.setList(criteria.list());
		return pager;
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#findByPager(com.csit.bean.Pager, org.hibernate.criterion.DetachedCriteria, java.lang.String[], java.lang.Object)
	 */
	public Pager findByPager(Pager pager, String[] propertys, T entity,Boolean countTotal) {

		if (pager == null) {
			pager = new Pager();
		}
		Integer pageSize = pager.getPageSize();
		if(pageSize==null||pageSize<0){
			pageSize = DEFAULTPAGESIZE;
		}
		String orderBy = pager.getOrderBy();
		OrderType orderType = pager.getOrderType();

		DetachedCriteria detachedCriteria = DetachedCriteria.forClass(entity.getClass());
		
		Criteria criteria = detachedCriteria.getExecutableCriteria(getSession());
		
		Map<String,String> map = new HashMap<String,String>();
		
		for (int i = 0; i < propertys.length; i++) {

			String property = propertys[i];
			Object value = null;
			if (StringUtils.isNotEmpty(property)) {
				if (property.contains(".")) {
					//保证只有一个"."，如果有两个点的话不用这种查询
					if (property.indexOf(".") == property.lastIndexOf(".")) {
						String propertyPrefix = StringUtils.substringBefore(property, ".");
						String propertySuffix = StringUtils.substringAfter(property, ".");
						//两次获取属性
						value = BeanUtil.getBeanPropertyValue(entity, propertyPrefix);
						value = BeanUtil.getBeanPropertyValue(value, propertySuffix);
						//判断别名是否已经存在,如果存在就不再重复创建
						if(map.get(propertyPrefix)==null&& value !=null){
							criteria.createAlias(propertyPrefix, propertyPrefix);
							map.put(propertyPrefix, propertyPrefix);
						}
						
					}
				} else {
					value = BeanUtil.getBeanPropertyValue(entity, property);
				}
				if (value != null && StringUtils.isNotEmpty(value.toString())) {
					criteria.add(Restrictions.like(property, "%" + value.toString() + "%"));
				}
			}
		}

		Long totalCount = 0L;
		
		if(countTotal){
			totalCount = (Long) criteria.setProjection(Projections.rowCount()).uniqueResult();
		}

		criteria.setProjection(null);
		criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		criteria.setFirstResult(pager.getBeginCount());
		criteria.setMaxResults(pageSize);
		
		if (StringUtils.isNotEmpty(orderBy) && orderType != null) {
			if(orderBy.contains(".")){
				String orderPrefix = StringUtils.substringBefore(orderBy, ".");
				if(map.get(orderPrefix)==null){
					criteria.createAlias(orderPrefix, orderPrefix);
				}
			}
			if (orderType == OrderType.asc) {
				criteria.addOrder(Order.asc(orderBy));
			} else {
				criteria.addOrder(Order.desc(orderBy));
			}
		}
		pager.setTotalCount(totalCount);
		pager.setList(criteria.list());
		return pager;
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.core.dao.BaseDao#find(java.lang.Integer, java.lang.Integer, java.lang.String[], java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	public List<T> find(Integer pageNumber, Integer pageSize, String[] propertys, T entity){
		
		if(pageNumber==null||pageNumber<1){
			pageNumber = 1;
		}
		if(pageSize==null||pageSize<0){
			pageSize = DEFAULTPAGESIZE;
		}
		
		Integer begin = (pageNumber-1)*pageSize;
		
		DetachedCriteria detachedCriteria = DetachedCriteria.forClass(entity.getClass());
		
		Criteria criteria = detachedCriteria.getExecutableCriteria(getSession());
		
		Map<String,String> map = new HashMap<String,String>();
		
		for (int i = 0; i < propertys.length; i++) {

			String property = propertys[i];
			Object value = null;
			if (StringUtils.isNotEmpty(property)) {
				if (property.contains(".")) {
					//保证只有一个"."，如果有两个点的话不用这种查询
					if (property.indexOf(".") == property.lastIndexOf(".")) {
						String propertyPrefix = StringUtils.substringBefore(property, ".");
						String propertySuffix = StringUtils.substringAfter(property, ".");
						//两次获取属性
						value = BeanUtil.getBeanPropertyValue(entity, propertyPrefix);
						value = BeanUtil.getBeanPropertyValue(value, propertySuffix);
						//判断别名是否已经存在,如果存在就不再重复创建
						if(map.get(propertyPrefix)==null&& value !=null){
							criteria.createAlias(propertyPrefix, propertyPrefix);
							map.put(propertyPrefix, propertyPrefix);
						}
						
					}
				} else {
					value = BeanUtil.getBeanPropertyValue(entity, property);
				}
				if (value != null && StringUtils.isNotEmpty(value.toString())) {
					criteria.add(Restrictions.like(property, "%" + value.toString() + "%"));
				}
			}
		}

		criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		criteria.setFirstResult(begin);
		criteria.setMaxResults(pageSize);
		
		return criteria.list();
	}
	/*
		 * (non-Javadoc)
		 * @see com.csit.dao.BaseDao#flush()
		 */
	public void flush() {
		this.getSession().flush();
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#get(java.io.Serializable)
	 */
	@SuppressWarnings("unchecked")
	public T get(PK id) {
		Assert.notNull(id, "id is required");

		T entity = (T) getSession().get(entityClass, id);

		return entity;
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#get(PK[])
	 */
	@SuppressWarnings("unchecked")
	public List<T> get(PK[] ids) {
		Assert.notEmpty(ids, "ids must not be empty");
		String hql = "from " + entityClass.getName() + " as model where model.id in(:ids)";
		return getSession().createQuery(hql).setParameterList("ids", ids).list();
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#get(java.lang.String, java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	public T get(String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(value, "value is required");
		String hql = "from " + entityClass.getName() + " as model where model." + propertyName + " = ?";
		return (T) getSession().createQuery(hql).setParameter(0, value).setFirstResult(0).setMaxResults(1).uniqueResult();
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#getAll()
	 */
	@SuppressWarnings("unchecked")
	public List<T> getAll() {
		String hql = "from " + entityClass.getName();
		return getSession().createQuery(hql).list();
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.core.dao.BaseDao#getList(java.lang.String, java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	public List<T> getList(String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(value, "value is required");
		String hql = "from " + entityClass.getName() + " as model where model." + propertyName + " = ?";
		return getSession().createQuery(hql).setParameter(0, value).list();
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.core.dao.BaseDao#getList(java.lang.String[], java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	public List<T> getList(String[] propertiesName, Object value){
		for(String propertyName: propertiesName){
			Assert.hasText(propertyName, "propertyName must not be empty");
		}
		Assert.notNull(value, "value is required");
		StringBuilder hql = new StringBuilder();
		hql.append("from ").append(entityClass.getName()).append(" as model where ");
		int idx =0;
		for(String propertyName: propertiesName){
			if(BeanUtil.getBeanPropertyValue(value, propertyName)!=null){
				if(idx>0){
					hql.append(" and ");
				}
				idx++;
				hql.append(" model.").append(propertyName).append("=:").append(propertyName);
			}
		}
		Query query = getSession().createQuery(hql.toString());
		
		Object object = null;
		for(String propertyName: propertiesName){
			object = BeanUtil.getBeanPropertyValue(value, propertyName);
			if(object!=null){
				query.setParameter(propertyName, object);
			}
		}
		
		return query.list();
	}
	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#getTotalCount()
	 */
	public Long getTotalCount() {
		String hql = "select count(*) from " + entityClass.getName();
		return (Long) getSession().createQuery(hql).uniqueResult();
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#isExist(java.lang.String, java.lang.Object)
	 */
	public boolean isExist(String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(value, "value is required");
		T object = get(propertyName, value);
		return (object != null);
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.core.dao.BaseDao#isExist(java.lang.String[], java.lang.Object)
	 */
	public boolean isExist(String[] propertiesName, Object value) {
		for(String propertyName: propertiesName){
			Assert.hasText(propertyName, "propertyName must not be empty");
		}
		Assert.notNull(value, "value is required");
		List<T> list = getList(propertiesName,value);
		//查询数据库，如果按照propertyName查询存在,表示数据库中已经有记录,返回false,否则true
		return (list.size() > 0);
	}
	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#isUnique(java.lang.String, java.lang.Object, java.lang.Object)
	 */
	public boolean isUnique(String propertyName, Object oldValue, Object newValue) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(newValue, "newValue is required");
		//旧值=新值,说明唯一
		if (newValue == oldValue || newValue.equals(oldValue)) {
			return true;
		}
		if (newValue instanceof String) {
			if (oldValue != null && StringUtils.equalsIgnoreCase((String) oldValue, (String) newValue)) {
				return true;
			}
		}
		T object = get(propertyName, newValue);
		//查询数据库，如果按照propertyName查询存在,表示数据库中已经有记录,返回false,否则true
		return (object == null);
	}
	/*
	 * (non-Javadoc)   
	 * @see com.csit.core.dao.BaseDao#isUnique(java.lang.String[], java.lang.Object, java.lang.Object)
	 */
	public boolean isUnique(String[] propertiesName, Object oldValue, Object newValue){
		for(String propertyName: propertiesName){
			Assert.hasText(propertyName, "propertyName must not be empty");
		}
		Assert.notNull(oldValue, "oldValue is required");
		Assert.notNull(newValue, "newValue is required");
		if (newValue == oldValue || newValue.equals(oldValue)) {
			return true;
		}
		if (newValue instanceof String) {
			if (oldValue != null && StringUtils.equalsIgnoreCase((String) oldValue, (String) newValue)) {
				return true;
			}
		}
		List<T> list = getList(propertiesName,newValue);
		//查询数据库，如果按照propertyName查询存在,表示数据库中已经有记录,返回false,否则true
		return (list.size() == 0);
	}
	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#load(java.io.Serializable)
	 */
	@SuppressWarnings("unchecked")
	public T load(PK id) {
		Assert.notNull(id, "id is required");
		return (T) getSession().load(entityClass, id);
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#save(java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	public PK save(T entity) {
		Assert.notNull(entity, "entity is required");
		return (PK) getSession().save(entity);
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#update(java.lang.Object)
	 */
	public void update(T entity) {
		Assert.notNull(entity, "entity is required");
		getSession().update(entity);
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#updateProperty(java.lang.String[], java.lang.Object)
	 */
	public void updateProperty(String[] propertys, T newEntity, T oldEntity) {

		Object value = null;

		for (String property : propertys) {
			//先获取属性的新值
			value = BeanUtil.getBeanPropertyValue(newEntity, property);
			//后更新属性的值
			BeanUtil.setBeanPropertyValue(oldEntity, property, value);
		}

		update(oldEntity);
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#getEntity(java.lang.String)
	 */
	public Object getEntity(String hql) {
		Assert.notNull(hql, "hql is required");
		return getSession().createQuery(hql).setFetchSize(1).uniqueResult();
	}

	/*
	 * (non-Javadoc)   
	 * @see com.csit.dao.BaseDao#getEntityList(java.lang.String)
	 */

	@SuppressWarnings("unchecked")
	public List<T> getEntityList(String hql) {
		Assert.notNull(hql, "hql is required");
		return (List<T>) getSession().createQuery(hql).list();
	}

	@SuppressWarnings("unchecked")
	public List<T> getEntityList(String hql, Object... params) {
		Assert.notNull(hql, "hql is required");
		Query query = getSession().createQuery(hql);
		for (int i = 0; i < params.length; i++) {
			Object param = params[i];
			query.setParameter(i  , param);
		}
		return (List<T>) query.list();
	}

	@SuppressWarnings("unchecked")
	public List<T> getList(Map<String, Object> params) {
		StringBuilder hql = new StringBuilder();
		hql.append("FROM ");
		hql.append(entityClass.getName());
		hql.append(" as model where ");
		int idx = 0;
		for (Map.Entry<String, Object> entry : params.entrySet()) {
			if (idx > 0) hql.append(" AND ");
			hql.append(" model.").append(entry.getKey()).append("=? ");
			idx++;
		}
		Query query = getSession().createQuery(hql.toString());

		idx = 0;
		for (Map.Entry<String, Object> entry : params.entrySet()) {

			query.setParameter(idx, entry.getValue());
			idx++;
		}
		return query.list();

	}
	/*
	 * (non-Javadoc)   
	 * @see com.csit.core.dao.BaseDao#merge(java.lang.Object)
	 */
	public void merge(T entity){
		getSession().merge(entity);
	}
}
